/*
 * 代号：凤凰
 * http://www.jphenix.org
 * 2014-06-13
 * V4.0
 */
package com.jphenix.webserver.instancea;

import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.net.InetAddress;
import java.net.ServerSocket;
import java.security.KeyStore;

import javax.net.ssl.KeyManager;
import javax.net.ssl.KeyManagerFactory;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLServerSocket;
import javax.net.ssl.TrustManager;
import javax.net.ssl.TrustManagerFactory;
import javax.net.ssl.X509TrustManager;

import com.jphenix.share.lang.SString;
import com.jphenix.share.tools.FileCopyTools;
import com.jphenix.share.util.BytesArrayUtil;
import com.jphenix.standard.docs.ClassInfo;


/**
 * SSLServerSocketFactory 无用
 * 
 * 2019-09-17 整理了代码格式
 * 2022-09-04 重构了内置Servlet容器，不依赖外部ServletApi包
 * 2023-12-20 增加了内置证书，不依赖外部证书文件。不依赖JDK环境变量实现SSL功能
 * 2024-06-05 去掉了文件处理类，无法将相对路径转为绝对路径，也没必要
 * 
 * @author 刘虻
 * 2007-3-6下午01:35:04
 */
@ClassInfo({"2024-06-05 11:12","SSLServerSocketFactory"})
public class SSLServerSocketFactory {
	
	private final String ARG_KEYSTOREFILE = "keystoreFile"; // Keystore 文件路径
	private final String ARG_KEYSTORETYPE = "keystoreType"; // Keystore 类型，默认PKCS12
	private final String ARG_SSLTYPE      = "sslType";      // SSL类型 类型，默认TLS
	private final String ARG_KEYSTOREPASS = "keystorePass"; // KEYSTOREPASS
	private final String ART_SSL_PORT     = "sslport";      // 安全会话监听端口
	private final int    DEF_SSLPORT      = 443;            // 默认安全端口
	private final String ARG_BACKLOG      = "backlog";      // 默认并发连接数参数
	private final String ARG_IFADDRESS    = "ifAddress";    // ifAddress
	private final String ARG_CLIENTAUTH   = "clientAuth";   // clientAuth
	private KeyStore         keyStore;      // 证书存储对象
	private X509TrustManager trustManager;  // 证书管理类
	
    /**
     * 构造函数
     * 2009-11-11上午10:37:42
     */
    public SSLServerSocketFactory() {
    	super();
    }
    
    /**
     * 覆盖方法
     * @author 刘虻
     * 2009-12-29上午11:17:56
     */
    public ServerSocket createSocket(ServeParameter serveParameter) throws Exception {
        //获取默认端口工厂
        javax.net.ServerSocketFactory serverSocketFactory = null;
    	//文件路径（绝对路径）
        String keystoreFile = serveParameter.getArgumentString(ARG_KEYSTOREFILE);
        // 证书密码
        char[] pwdChars;
        if (keystoreFile.length()<1) {
        	pwdChars = ServeParameter.DEFAULT_KEY_PASSWD.toCharArray();
        	keyStore = KeyStore.getInstance("PKCS12");
        	keyStore.load(new ByteArrayInputStream(BytesArrayUtil.hex2Bytes(ServeParameter.DEFAULT_KEYSTORE)),pwdChars);
        	SSLContext context = SSLContext.getInstance("TLS");
        	context.init(getKeyManagers(pwdChars),getTrustManagers(),null);
        	serverSocketFactory = context.getServerSocketFactory();
        }else {
        	pwdChars = SString.valueOf(serveParameter.getArgumentString(ARG_KEYSTOREPASS)).toCharArray();
        	keyStore     = KeyStore.getInstance(SString.valueOf(serveParameter.getArgumentString(ARG_KEYSTORETYPE),"PKCS12"));
        	keyStore.load(new ByteArrayInputStream(FileCopyTools.copyToByteArray(keystoreFile)),pwdChars);
        	SSLContext context = SSLContext.getInstance(SString.valueOf(serveParameter.getArgumentString(ARG_SSLTYPE),"TLS"));
        	context.init(getKeyManagers(pwdChars),getTrustManagers(),null);
        	serverSocketFactory = context.getServerSocketFactory();
        }
        //获取监听端口
        int port = serveParameter.getArgumentInt(ART_SSL_PORT);
        if (port<1) {
        	port = DEF_SSLPORT;
        }
        int backlog = serveParameter.getArgumentInt(ARG_BACKLOG);
        //绑定地址
        String ifAddress = serveParameter.getArgumentString(ARG_IFADDRESS);

        SSLServerSocket secureSocket = null;
        try {
        	if(ifAddress.length()>0) {
            	secureSocket = 
            		(SSLServerSocket)serverSocketFactory
            			.createServerSocket(port,backlog,InetAddress.getByName(ifAddress));
        	}else {
            	secureSocket = 
            		(SSLServerSocket)serverSocketFactory
            			.createServerSocket(port,backlog);
        	}
        } catch (Exception e) {
            e.printStackTrace();
			//构建错误信息
			StringBuffer errorMsg = new StringBuffer();
			
			errorMsg
				.append("\n\nXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXx\n")
				.append("Create SSLSocket Port:[")
				.append(port)
				.append("] Backlog:[")
				.append(backlog)
				.append("] BindAddress:[")
				.append(ifAddress)
				.append("] Exception:[")
				.append(e)
				.append("]\n")
				.append("XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX\n\n\n");
			System.err.print(errorMsg);
            throw new IOException(e.toString());
        }
        //设置匿名访问
        String[] cipherSuites = secureSocket.getSupportedCipherSuites();
        secureSocket.setEnabledCipherSuites(cipherSuites);
        secureSocket.setNeedClientAuth(serveParameter.getArgumentBoolean(ARG_CLIENTAUTH));
        return secureSocket;
    }
    
    /**
     * 获取密钥管理类
     * @param pwd 证书密码
     * @return 密钥管理类
     * 2023年12月20日
     * @author MBG
     */
	public KeyManager[] getKeyManagers(char[] pwd) {
        try {
            KeyManagerFactory kmf = KeyManagerFactory.getInstance("SunX509");
            kmf.init(keyStore, pwd);
            return kmf.getKeyManagers();
        } catch (Exception e) {
        	e.printStackTrace();
        }
        return null;
    }
    
    /**
     * 获取证书管理类
     * @return 证书管理类
     * 2023年12月20日
     * @author MBG
     */
    private TrustManager[] getTrustManagers() {
    	try {
        	TrustManagerFactory tmf = 
        			TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
        	tmf.init(keyStore);
            TrustManager tms[] = tmf.getTrustManagers();
            for (int i = 0; i < tms.length; i++) {
                if (tms[i] instanceof X509TrustManager) {
                	trustManager = (X509TrustManager) tms[i];
                    break;
                }
            }
    	}catch(Exception e) {
    		e.printStackTrace();
    	}
    	return new TrustManager[]{ trustManager };
    }
}
